using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using AntEater.ComponentModel.BuildElements;
using AntEater.ComponentModel;
using System.Reflection;
using System.Diagnostics.CodeAnalysis;

namespace AntEater.Runtime.UI
{
    /// <summary>
    /// 
    /// </summary>
    [Icon(typeof(BuildFileView),"AntEater.Runtime.Resources.AntEater.ico",Key="Default")]
    [Icon(typeof(BuildFileView), "AntEater.Runtime.Resources.CodeView.ico", Key = "CodeView")]
    [Icon(typeof(BuildFileView), "AntEater.Runtime.Resources.Copy.ico", Key = "Copy")]
    [Icon(typeof(BuildFileView), "AntEater.Runtime.Resources.Cut.ico", Key = "Cut")]
    [Icon(typeof(BuildFileView), "AntEater.Runtime.Resources.PasteChild.ico", Key = "PasteChild")]
    [Icon(typeof(BuildFileView), "AntEater.Runtime.Resources.PasteBefore.ico", Key = "PasteBefore")]
    [Icon(typeof(BuildFileView), "AntEater.Runtime.Resources.PasteAfter.ico", Key = "PasteAfter")]
    [Icon(typeof(BuildFileView), "AntEater.Runtime.Resources.Remove.ico", Key = "Delete")]
    [Icon(typeof(BuildFileView), "AntEater.Runtime.Resources.BuildFileAdd.ico", Key = "OpenBuildFile")]
    internal class BuildElementContextMenuStrip : ContextMenuStrip
    {

        /// <summary>
        /// Raised when the user wants to open a build file.
        /// </summary>
        public event EventHandler<BuildFileViewEventArgs> OpenFile;

        /// <summary>
        /// Raised when the user wants to Add a new BuildElement.
        /// </summary>
        public event EventHandler<AddBuildElementEventArgs> AddBuildElement;

        /// <summary>
        /// Raised when the user wants to delete a new BuildElement.
        /// </summary>
        public event EventHandler<EventArgs> DeleteBuildElement;

        /// <summary>
        /// Raised when the user wants to cut a new BuildElement.
        /// </summary>
        public event EventHandler<EventArgs> CutBuildElement;

        /// <summary>
        /// Raised when the user wants to copy a new BuildElement.
        /// </summary>
        public event EventHandler<EventArgs> CopyBuildElement;

        /// <summary>
        /// Raised when the user wants to Add a new BuildElement.
        /// </summary>
        public event EventHandler<PasteBuildElementEventArgs> PasteBuildElement;

        /// <summary>
        /// Initializes a new instance of the <see cref="T:BuildElementContextMenuStrip"/> class.
        /// </summary>
        /// <param name="buildElement">The build element.</param>
        /// <param name="copyPasteBuffer">The copy paste buffer.</param>
        public BuildElementContextMenuStrip(IBuildElement buildElement,EditBuffer copyPasteBuffer) {

            if (buildElement == null) { throw new ArgumentNullException("buildElement"); }

            this._buildElement = buildElement;
            this._editBuffer = copyPasteBuffer;
        }

        private static ImageList _buildElementImageList;

        /// <summary>
        /// Gets the build element image list.
        /// </summary>
        /// <value>The build element image list.</value>
        public static ImageList BuildElementImageList {
            get {
                if (_buildElementImageList == null) {
                    _buildElementImageList = BuildElementContextMenuStrip.CreateImageList(); 
                }
                return _buildElementImageList; 
            }
        }

        private IBuildElement _buildElement;

        /// <summary>
        /// Gets or sets the build element.
        /// </summary>
        /// <value>The build element.</value>
        public IBuildElement BuildElement {
            get { return _buildElement; }
        }

        private EditBuffer _editBuffer;

        /// <summary>
        /// Gets the edit buffer.
        /// </summary>
        /// <value>The edit buffer.</value>
        public EditBuffer EditBuffer {
            get { return _editBuffer; }
        }

        /// <summary>
        /// Called when [open file].
        /// </summary>
        /// <param name="buildFilePath">The build file path.</param>
        /// <param name="view">The view type.</param>
        protected void OnOpenFile(string buildFilePath,BuildFileViewType view ) {
            if (this.OpenFile != null) {
                this.OpenFile(this, new BuildFileViewEventArgs(buildFilePath, view));
            }
        }

        /// <summary>
        /// Called when [add build element].
        /// </summary>
        /// <param name="action">The action.</param>
        /// <param name="containerBuildElement">The container build element.</param>
        /// <param name="referenceBuildElement">The reference build element.</param>
        /// <param name="buildElementType">Type of the build element.</param>
        protected void OnAddBuildElement(AddAction action, IContainerBuildElement containerBuildElement, IBuildElement referenceBuildElement, Type buildElementType) {
            if (this.AddBuildElement != null) {
                this.AddBuildElement(this,new AddBuildElementEventArgs(action,containerBuildElement,referenceBuildElement,buildElementType));
            }
        }

        /// <summary>
        /// Called when [delete build element].
        /// </summary>
        protected void OnDeleteBuildElement() {
            if (this.DeleteBuildElement != null) {
                this.DeleteBuildElement(this, new EventArgs());
            }
        }

        /// <summary>
        /// Called when [cut build element].
        /// </summary>
        protected void OnCutBuildElement() {
            if (this.CutBuildElement != null) {
                this.CutBuildElement(this, new EventArgs());
            }
        }

        /// <summary>
        /// Called when [copy build element].
        /// </summary>
        protected void OnCopyBuildElement() {
            if (this.CopyBuildElement != null) {
                this.CopyBuildElement(this, new EventArgs());
            }
        }

        /// <summary>
        /// Called when [copy build element].
        /// </summary>
        protected void OnPasteBuildElement(PasteAction action, IContainerBuildElement containerBuildElement, IBuildElement referenceBuildElement) {
            if (this.PasteBuildElement != null) {
                this.PasteBuildElement(this, new PasteBuildElementEventArgs(action,containerBuildElement,referenceBuildElement));
            }
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.ToolStripDropDown.Opening"></see> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.ComponentModel.CancelEventArgs"></see> that contains the event data.</param>
        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        protected override void OnOpening(System.ComponentModel.CancelEventArgs e) {

            this.Items.Clear();

            if (this.BuildElement != null) {

                ToolStripMenuItem openBuildFileMenuItem = null;
                ToolStripMenuItem viewCodeMenuItem = null;
                ToolStripSeparator separator1 = null;
                ToolStripMenuItem addChildMenuItem = null;
                ToolStripMenuItem addBeforeMenuItem = null;
                ToolStripMenuItem addAfterMenuItem = null;
                ToolStripSeparator separator2 = null;
                ToolStripMenuItem cutMenuItem = null;
                ToolStripMenuItem copyMenuItem = null;
                ToolStripMenuItem pasteChildMenuItem = null;
                ToolStripMenuItem pasteBeforeMenuItem = null;
                ToolStripMenuItem pasteAfterMenuItem = null;
                ToolStripMenuItem deleteMenuItem = null;

                IContainerBuildElement container;

                //Open Build File
                OpenBuildFileActionAttribute attribute = Utilities.GetTypeAttributeFromObject<OpenBuildFileActionAttribute>(this.BuildElement);
                if (attribute != null) {
                    openBuildFileMenuItem = new ToolStripMenuItem(attribute.ActionText);
                    openBuildFileMenuItem.Image = BuildElementContextMenuStrip.BuildElementImageList.Images["OpenBuildFile"];
                    openBuildFileMenuItem.Click += new EventHandler(OpenBuildFileMenuItemClick);
                }

                //Create the following Menu Items if the BuildElement is a Container
                container = this.BuildElement as IContainerBuildElement;
                if (container != null) {

                    //Add Child Menu Item
                    addChildMenuItem = BuildElementContextMenuStrip.CreateValidChildElementMenuItem(container, null, "Add Child", new EventHandler(this.AddChildElement));

                    //Paste Child Menu Item
                    pasteChildMenuItem = new ToolStripMenuItem("Paste Child");
                    pasteChildMenuItem.Image = BuildElementContextMenuStrip.BuildElementImageList.Images["PasteChild"];

                    if (!this.EditBuffer.IsEmpty && (((this.EditBuffer.EditAction == EditAction.Cut) && container.ChildElements.Contains(this.EditBuffer.BuildElement)) || container.CanInsert(this.EditBuffer.BuildElement.GetType()))) {
                        pasteChildMenuItem.Tag = new BuildElementToolStripMenuItemData(container);
                        pasteChildMenuItem.Click += new EventHandler(PasteChildMenuItemClick);
                    }
                    else {
                        pasteChildMenuItem.Enabled = false;
                    }

                }

                container = this.BuildElement.Parent as IContainerBuildElement;

                if (container != null) {

                    //Add Before Menu Item
                    addBeforeMenuItem = BuildElementContextMenuStrip.CreateValidChildElementMenuItem(container, this.BuildElement, "Add Before", new EventHandler(this.AddElementBefore));

                    //Add After Menu Item
                    addAfterMenuItem = BuildElementContextMenuStrip.CreateValidChildElementMenuItem(container, this.BuildElement, "Add After", new EventHandler(this.AddElementAfter));

                    //Paste Before Menu Item
                    pasteBeforeMenuItem = new ToolStripMenuItem("Paste Before");
                    pasteBeforeMenuItem.Image = BuildElementContextMenuStrip.BuildElementImageList.Images["PasteBefore"];

                    if (!this.EditBuffer.IsEmpty && (((this.EditBuffer.EditAction == EditAction.Cut) && container.ChildElements.Contains(this.EditBuffer.BuildElement)) || container.CanInsert(this.EditBuffer.BuildElement.GetType()))) {
                        pasteBeforeMenuItem.Tag = new BuildElementToolStripMenuItemData(container, this.BuildElement);
                        pasteBeforeMenuItem.Click += new EventHandler(PasteBeforeMenuItemClick);
                    }
                    else {
                        pasteBeforeMenuItem.Enabled = false;
                    }

                    //Paste After Menu Item
                    pasteAfterMenuItem = new ToolStripMenuItem("Paste After");
                    pasteAfterMenuItem.Image = BuildElementContextMenuStrip.BuildElementImageList.Images["PasteAfter"];

                    if (!this.EditBuffer.IsEmpty && (((this.EditBuffer.EditAction == EditAction.Cut) && container.ChildElements.Contains(this.EditBuffer.BuildElement)) || container.CanInsert(this.EditBuffer.BuildElement.GetType()))) {
                        pasteAfterMenuItem.Tag = new BuildElementToolStripMenuItemData(container, this.BuildElement);
                        pasteAfterMenuItem.Click += new EventHandler(PasteAfterMenuItemClick);
                    }
                    else {
                        pasteAfterMenuItem.Enabled = false;
                    }
                }

                //View Code Menu Item
                viewCodeMenuItem = new ToolStripMenuItem("View Code");
                viewCodeMenuItem.Image = BuildElementContextMenuStrip.BuildElementImageList.Images["CodeView"];
                viewCodeMenuItem.Click += new EventHandler(ViewCodeMenuItemClick);

                separator1 = new ToolStripSeparator();

                //Only Create an Separator if one of the Add... Menu Items is present
                if (addChildMenuItem != null || addBeforeMenuItem != null || addAfterMenuItem != null) {
                    separator2 = new ToolStripSeparator();
                }

                //Cut Menu Item
                cutMenuItem = new ToolStripMenuItem("Cut");
                cutMenuItem.Image = BuildElementContextMenuStrip.BuildElementImageList.Images["Cut"];
                cutMenuItem.Click += new EventHandler(CutMenuItemClick);

                //Copy Menu Item
                copyMenuItem = new ToolStripMenuItem("Copy");
                copyMenuItem.Image = BuildElementContextMenuStrip.BuildElementImageList.Images["Copy"];
                copyMenuItem.Click += new EventHandler(CopyMenuItemClick);

                //Delete Menu Item
                deleteMenuItem = new ToolStripMenuItem("Delete");
                deleteMenuItem.Image = BuildElementContextMenuStrip.BuildElementImageList.Images["Delete"];
                deleteMenuItem.Click += new EventHandler(DeleteMenuItemClick);

                //Add Menu Items
                if (openBuildFileMenuItem != null) {
                    this.Items.Add(openBuildFileMenuItem);
                }
                if (viewCodeMenuItem != null) {
                    this.Items.Add(viewCodeMenuItem);
                }
                if (separator1 != null) {
                    this.Items.Add(separator1);
                }
                if (addChildMenuItem != null) {
                    this.Items.Add(addChildMenuItem);
                }
                if (addBeforeMenuItem != null) {
                    this.Items.Add(addBeforeMenuItem);
                }
                if (addAfterMenuItem != null) {
                    this.Items.Add(addAfterMenuItem);
                }
                if (!(this.BuildElement is IRootBuildElement)) {
                    if (separator2 != null) {
                        this.Items.Add(separator2);
                    }
                    if (cutMenuItem != null) {
                        this.Items.Add(cutMenuItem);
                    }
                    if (copyMenuItem != null) {
                        this.Items.Add(copyMenuItem);
                    }
                    if (pasteChildMenuItem != null) {
                        this.Items.Add(pasteChildMenuItem);
                    }
                    if (pasteBeforeMenuItem != null) {
                        this.Items.Add(pasteBeforeMenuItem);
                    }
                    if (pasteAfterMenuItem != null) {
                        this.Items.Add(pasteAfterMenuItem);
                    }
                    if (deleteMenuItem != null) {
                        this.Items.Add(deleteMenuItem);
                    }
                }

                e.Cancel = false;
            }

            base.OnOpening(e);
        }

        /// <summary>
        /// Creates the tree image list.
        /// </summary>
        /// <returns></returns>
        private static ImageList CreateImageList() {
            ImageList imageList = new ImageList();

            imageList.ColorDepth = ColorDepth.Depth32Bit;
            imageList.ImageSize = new System.Drawing.Size(16, 16);

            IconAttribute[] iconAttributes = Utilities.GetTypeAttributesFromType<IconAttribute>(typeof(BuildElementContextMenuStrip));

            foreach (IconAttribute iconAttribute in iconAttributes) {
                System.Drawing.Image defaultImage = iconAttribute.Icon;

                if (defaultImage != null) {
                    imageList.Images.Add(iconAttribute.Key, defaultImage);
                }
            }

            return imageList;
        }

        /// <summary>
        /// Gets the build element image key.
        /// </summary>
        /// <param name="buildElementType">Type of the build element.</param>
        /// <returns></returns>
        private static string GetBuildElementImageKey(Type buildElementType) {
            string key = buildElementType.FullName;

            if (!BuildElementContextMenuStrip.BuildElementImageList.Images.ContainsKey(key)) {
                IconAttribute iconAttribute = Utilities.GetTypeAttributeFromType<IconAttribute>(buildElementType);

                if (iconAttribute != null) {
                    System.Drawing.Image icon = iconAttribute.Icon;

                    if (icon != null) {
                        BuildElementContextMenuStrip.BuildElementImageList.Images.Add(key, icon);
                    }
                    else {
                        key = "Default";
                    }
                }
                else {
                    key = "Default";
                }
            }

            return key;
        }

        /// <summary>
        /// Creates the valid child element menu item.
        /// </summary>
        /// <param name="containerElement">The container element.</param>
        /// <param name="referenceElement">The reference element.</param>
        /// <param name="menuItemText">The menu item text.</param>
        /// <param name="menuItemClickEventHandler">The menu item click event handler.</param>
        /// <returns></returns>
        private static ToolStripMenuItem CreateValidChildElementMenuItem(IContainerBuildElement containerElement, IBuildElement referenceElement, string menuItemText, EventHandler menuItemClickEventHandler) {
            ToolStripMenuItem addChildElementRootMenuItem = new ToolStripMenuItem();

            addChildElementRootMenuItem.Text = menuItemText;

            ChildBuildElementTypeAttribute[] attributes
                = AntEater.ComponentModel.Utilities.GetTypeAttributesFromObject<ChildBuildElementTypeAttribute>(containerElement);

            if (attributes != null && attributes.Length > 0) {
                foreach (ChildBuildElementTypeAttribute attribute in attributes) {

                    if (containerElement.CanInsert(attribute.BuildElementType)) {

                        BuildElementAttribute buildElementAttribute
                            = AntEater.ComponentModel.Utilities.GetTypeAttributeFromType<BuildElementAttribute>(attribute.BuildElementType);

                        if (buildElementAttribute != null) {
                            ToolStripMenuItem addChildElementMenuItem = new ToolStripMenuItem();
                            addChildElementMenuItem.Text = buildElementAttribute.Name;
                            addChildElementMenuItem.Click += menuItemClickEventHandler;
                            addChildElementMenuItem.Tag = new BuildElementToolStripMenuItemData(containerElement, referenceElement, attribute.BuildElementType);
                            addChildElementMenuItem.Image = BuildElementContextMenuStrip.BuildElementImageList.Images[BuildElementContextMenuStrip.GetBuildElementImageKey(attribute.BuildElementType)];
                            
                            addChildElementRootMenuItem.DropDownItems.Add(addChildElementMenuItem);
                        }
                    }
                }
            }
            return addChildElementRootMenuItem;
        }

        /// <summary>
        /// Opens the build file menu item click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void OpenBuildFileMenuItemClick(object sender, EventArgs e) {
            OpenBuildFileActionAttribute attribute = Utilities.GetTypeAttributeFromObject<OpenBuildFileActionAttribute>(this.BuildElement);

            if (attribute != null) {
                PropertyInfo propertyInfo = this.BuildElement.GetType().GetProperty(attribute.BuildFilePathPropertyName);

                if (propertyInfo != null) {
                    string buildFilePath = propertyInfo.GetValue(this.BuildElement, null) as string;

                    if (!string.IsNullOrEmpty(buildFilePath)) {
                        this.OnOpenFile(buildFilePath, BuildFileViewType.Designer);
                    }
                }
            }
        }

        /// <summary>
        /// Views the code menu item click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void ViewCodeMenuItemClick(object sender, EventArgs e) {
            this.OnOpenFile(this.BuildElement.Root.BuildFilePath, BuildFileViewType.Xml);
        }

        /// <summary>
        /// Deletes the menu item click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void DeleteMenuItemClick(object sender, EventArgs e) {
            this.OnDeleteBuildElement();
        }

        /// <summary>
        /// Copies the menu item click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void CopyMenuItemClick(object sender, EventArgs e) {
            this.OnCopyBuildElement();
        }

        /// <summary>
        /// Cuts the menu item click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void CutMenuItemClick(object sender, EventArgs e) {
            this.OnCutBuildElement();
        }

        /// <summary>
        /// Pastes the after menu item click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void PasteAfterMenuItemClick(object sender, EventArgs e) {
            ToolStripMenuItem menuItem = sender as ToolStripMenuItem;

            if (menuItem != null) {
                BuildElementToolStripMenuItemData menuItemData = menuItem.Tag as BuildElementToolStripMenuItemData;

                if (menuItemData != null) {
                    if (menuItemData.Container != null && menuItemData.ReferenceBuildElement != null) {
                        this.OnPasteBuildElement(PasteAction.PasteAfterReference, menuItemData.Container, menuItemData.ReferenceBuildElement);
                    }
                }
            }
        }

        /// <summary>
        /// Pastes the before menu item click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void PasteBeforeMenuItemClick(object sender, EventArgs e) {
            ToolStripMenuItem menuItem = sender as ToolStripMenuItem;

            if (menuItem != null) {
                BuildElementToolStripMenuItemData menuItemData = menuItem.Tag as BuildElementToolStripMenuItemData;

                if (menuItemData != null) {
                    if (menuItemData.Container != null && menuItemData.ReferenceBuildElement != null) {
                        this.OnPasteBuildElement(PasteAction.PasteBeforeReference, menuItemData.Container, menuItemData.ReferenceBuildElement);
                    }
                }
            }
        }

        /// <summary>
        /// Pastes the child menu item click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void PasteChildMenuItemClick(object sender, EventArgs e) {
            ToolStripMenuItem menuItem = sender as ToolStripMenuItem;

            if (menuItem != null) {
                BuildElementToolStripMenuItemData menuItemData = menuItem.Tag as BuildElementToolStripMenuItemData;

                if (menuItemData != null) {
                    if (menuItemData.Container != null) {
                        this.OnPasteBuildElement(PasteAction.PasteChild, menuItemData.Container, null);
                    }
                }
            }
        }


        /// <summary>
        /// Adds the child element.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void AddChildElement(object sender, EventArgs e) {
            ToolStripMenuItem menuItem = sender as ToolStripMenuItem;

            if (menuItem != null) {
                BuildElementToolStripMenuItemData menuItemData = menuItem.Tag as BuildElementToolStripMenuItemData;

                if (menuItemData != null) {
                    if (menuItemData.Container != null && menuItemData.BuildElementType != null) {
                        this.OnAddBuildElement(AddAction.AddChild, menuItemData.Container, null, menuItemData.BuildElementType);
                    }
                }
            }
        }

        /// <summary>
        /// Adds the element before.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void AddElementBefore(object sender, EventArgs e) {
            ToolStripMenuItem menuItem = sender as ToolStripMenuItem;

            if (menuItem != null) {
                BuildElementToolStripMenuItemData menuItemData = menuItem.Tag as BuildElementToolStripMenuItemData;

                if (menuItemData != null) {

                    if (menuItemData.Container != null && menuItemData.BuildElementType != null && menuItemData.ReferenceBuildElement != null) {
                        this.OnAddBuildElement(AddAction.AddBeforeReference, menuItemData.Container, menuItemData.ReferenceBuildElement, menuItemData.BuildElementType);
                    }
                }
            }
        }

        /// <summary>
        /// Adds the element after.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void AddElementAfter(object sender, EventArgs e) {
            ToolStripMenuItem menuItem = sender as ToolStripMenuItem;

            if (menuItem != null) {
                BuildElementToolStripMenuItemData menuItemData = menuItem.Tag as BuildElementToolStripMenuItemData;

                if (menuItemData != null) {

                    if (menuItemData.Container != null && menuItemData.BuildElementType != null && menuItemData.ReferenceBuildElement != null) {
                        this.OnAddBuildElement(AddAction.AddAfterReference, menuItemData.Container, menuItemData.ReferenceBuildElement, menuItemData.BuildElementType);
                    }
                }
            }
        }
    }
}
